#include "vm.h"
#include <math.h>
#include <iostream>
using namespace std;

namespace logo
{
	lvmplugins::lvmplugins()
	{
	}

	lvmplugins::~lvmplugins()
	{}

	lvmplugins& lvmplugins::operator=(const lvmplugins& rhs)
	{
		function = rhs.function;
		name = rhs.name;
		argc = rhs.argc;
		return *this;
	}

	ShapeBrush::ShapeBrush()
	{
#ifdef _MSC_VER
#else
		surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 800, 600);
		cr = cairo_create(surface);
		cairo_set_source_rgb(cr, 0, 0, 0);
		cairo_set_line_width(cr, 1.0);
		startx = 400.0;
		starty = 300.0;
		angle = 90.0;
		cairo_move_to(cr, startx, starty);
#endif
	}

	ShapeBrush::~ShapeBrush()
	{
#ifdef _MSC_VER
#else
		cairo_surface_write_to_png(surface, "logo.png");
		cairo_destroy(cr);
		cairo_surface_destroy(surface);
#endif
	}

	void ShapeBrush::RT(double _angle)
	{
		angle -= _angle;
	}

	void ShapeBrush::LT(double _angle)
	{
		angle += _angle;
	}

	void ShapeBrush::FD(double len)
	{
#ifdef _MSC_VER
#else
		startx += len*cos(angle / 180.0*M_PI);
		starty += len*sin(angle / 180.0*M_PI);
		cairo_line_to(cr, startx, starty);
		cairo_stroke(cr);
		cairo_move_to(cr, startx, starty);
#endif
	}

	lvm::lvm()
	{
		stop = false;
		brush = new ShapeBrush;
	}

	lvm::~lvm()
	{}

	void lvm::Register()
	{
		lvmplugins stop;
		stop.argc = 0;
		stop.name = L"STOP";
		stop.function = [&](List<double>& argv, bool& flag, Ptr<ShapeBrush>& brush){cout << "STOP" << endl; flag = true; };
		cmd.add(stop.name, stop);
		lvmplugins fd;
		fd.argc = 1;
		fd.name = L"FD";
		fd.function = [&](List<double>& argv, bool& flag, Ptr<ShapeBrush>& brush){
			cout << "FD:" << argv[0] << endl;
			brush->FD(argv[0]);
		};
		cmd.add(fd.name, fd);
		lvmplugins rt;
		rt.argc = 1;
		rt.name = L"RT";
		rt.function = [&](List<double>& argv, bool& flag, Ptr<ShapeBrush>& brush){
			cout << "RT:" << argv[0] << endl;
			brush->RT(argv[0]);
		};
		cmd.add(rt.name, rt);
		lvmplugins lt;
		lt.argc = 1;
		lt.name = L"LT";
		lt.function = [&](List<double>& argv, bool& flag, Ptr<ShapeBrush>& brush){
			cout << "LT:" << argv[0] << endl;
			brush->LT(argv[0]);
		};
	}

	bool lvm::Init(const std::wstring& name)
	{
                FileStream  file(name, FileStream::AccessRight::ReadWrite);
		pos_t size = file.Size();
                if(size<=0) return false;
		char* buff = new char[(zuint)size + 1];
		file.Read(buff, (zint)size);
		buff[size] = '\0';
		std::wstring inputs = atow(buff);
		delete buff;
		buff = nullptr;
		if (inputs.length() == 0) return false;
		Register();
		codes = CodeFile::Parse(inputs, lexerrs);
		if (lexerrs.size()>0) return false;
		zint rows = 0;
		ParseArea(codes->lines, rows, expressions, globalenv, expressionerrs);
		if (expressionerrs.size()>0) return false;
		return true;
	}

	void lvm::RunExp(Ptr<Expression> exp, Dictionary<std::wstring, AssignmentExpression>& env, bool& flag)
	{
		if (flag) return;
		switch (exp->type)
		{
		case ExpressionType::Arithmetic:
			break;
		case ExpressionType::Assignment:
		{
			AssignmentExpression* ptr = dynamic_cast<AssignmentExpression*>(exp.Obj());
			globalenv.add(ptr->name, AssignmentExpression(ptr->name, ptr->value));
		}
			break;
		case ExpressionType::Select:
		{
		     SelectExpression* ptr = dynamic_cast<SelectExpression*>(exp.Obj());
			 if (ptr->value)
			 {
				for(auto ex:ptr->tbranch)
				{
					RunExp(ex, env, flag);
				}
			  }
			  else
			  {
				 for(auto ex:ptr->fbranch)
				 {
					 RunExp(ex, env, flag);
				 }
			  }
		 }
			break;
		case ExpressionType::Loop:
		{
		    LoopExpression* ptr = dynamic_cast<LoopExpression*>(exp.Obj());
			for (zint i = 0; i<ptr->loops; i++)
			{
			   for(auto ex:ptr->proc)
			   {
				  RunExp(ex, env, flag);
			   }
			}
		}
			break;
		case ExpressionType::Cmd:
		{
			CmdExpression* ptr = dynamic_cast<CmdExpression*>(exp.Obj());
			lvmplugins plugin = cmd[ptr->name];
			if (ptr->args.size() != plugin.argc)
			{
				break;
			}
			plugin.function(ptr->args, flag, brush);
		}
			break;
		case ExpressionType::Func:
		{
			FuncExpression* ptr = dynamic_cast<FuncExpression*>(exp.Obj());
			List<std::wstring>  name;
			name = from(funcs.keys()).where([&](std::wstring name){return name == ptr->name; })
				.to_zl_list();
		    if (name.size() == 0)
			{
				break;
			}
			DefFuncExpression* def_ptr = dynamic_cast<DefFuncExpression*>(funcs[ptr->name].Obj());
			ptr->env = globalenv;
			for (zint i = 0; i<def_ptr->args.size(); i++)
			{
			   ptr->env.add(def_ptr->args[i], AssignmentExpression(def_ptr->args[i], ptr->args[i]));
			}
		    for (zint i = def_ptr->start; i<def_ptr->end; i++)
			{
			    zint j = 0;
				ParseUnit(codes->lines[i].tokens, j, ptr->proc, ptr->env, expressionerrs);
			}
			for(auto ex:ptr->proc)
			{
				RunExp(ex, ptr->env, ptr->isend);
			}
		}
			break;
		case ExpressionType::Defunc:
		{
			DefFuncExpression* ptr = dynamic_cast<DefFuncExpression*>(exp.Obj());
			funcs.add(ptr->name, exp);
		}
			break;
		}
	}

	void lvm::Run()
	{
		for(auto exp:expressions)
		{
			RunExp(exp, globalenv, stop);
		}
	}
};
